package com.starlinkdt.common.utils.oaiauth;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.util.*;

public class OaiServAuthSignUtils {
    private static final Logger log = LoggerFactory.getLogger(OaiServAuthSignUtils.class);
    public static final String SIGN_FIELD = "sign";
    private static final String SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    private static final Random RANDOM = new SecureRandom();

    public static boolean isSignValid(Map<String, String> data, String key, SignType signType) throws Exception {
        if (!data.containsKey(SIGN_FIELD)) {
            return false;
        }
        String sign = (String)data.get(SIGN_FIELD);
        return genSign(data, key, signType).equalsIgnoreCase(sign);
    }

    public static String genSign(Map<String, String> data, String key) throws Exception {
        return genSign(data, key, SignType.MD5);
    }

    public static String genSign(Map<String, String> data, String key, SignType signType) throws Exception {
        Set<String> keySet = data.keySet();
        String[] keyArray = (String[])keySet.toArray(new String[keySet.size()]);
        Arrays.sort(keyArray);
        StringBuilder sb = new StringBuilder();
        for (String k : keyArray) {
            if (!k.equals(SIGN_FIELD)) {
                String value = String.valueOf(data.get(k));
                sb.append(k).append("=").append(value.trim());
            }
        }
        sb.append("key=").append(key);
        String sign = "";
        if (SignType.MD5.equals(signType)) {
            sign = MD5(sb.toString()).toUpperCase();
        } else if (SignType.HMACSHA256.equals(signType)) {
            sign = HMACSHA256(sb.toString(), key);
        } else {
            throw new Exception(String.format("Invalid sign_type: %s", new Object[] { signType }));
        }

        // 记录日志
        String logFormat = "OaiServAuthSignUtils：[SIGN_PARAMS : {}] [SIGN_STRING : {}] [SIGN_VERIFY : {}]";
        List<String> logArg = new ArrayList<>();
        logArg.add(data.toString());
        logArg.add(sb.toString());
        logArg.add(sign);
        log.info(logFormat, logArg.toArray());

        return sign;
    }

    public static String genNonceStr() {
        char[] nonceChars = new char[32];
        for (int index = 0; index < nonceChars.length; index++) {
            nonceChars[index] = SYMBOLS.charAt(RANDOM.nextInt(SYMBOLS.length()));
        }
        return new String(nonceChars);
    }

    public static String MD5(String data) throws Exception {
        MessageDigest md = MessageDigest.getInstance("MD5");
        byte[] array = md.digest(data.getBytes(StandardCharsets.UTF_8));
        StringBuilder sb = new StringBuilder();
        for (byte item : array) {
            sb.append(Integer.toHexString(item & 0xFF | 0x100), 1, 3);
        }
        return sb.toString().toUpperCase();
    }

    public static String HMACSHA256(String data, String key) throws Exception {
        Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
        SecretKeySpec secret_key = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
        sha256_HMAC.init(secret_key);
        byte[] array = sha256_HMAC.doFinal(data.getBytes(StandardCharsets.UTF_8));
        StringBuilder sb = new StringBuilder();
        for (byte item : array) {
            sb.append(Integer.toHexString(item & 0xFF | 0x100), 1, 3);
        }
        return sb.toString().toUpperCase();
    }

    public static long getCurrentTimestamp() {
        return System.currentTimeMillis() / 1000L;
    }

    public static long getCurrentTimestampMs() {
        return System.currentTimeMillis();
    }

    public enum SignType
    {
        MD5, HMACSHA256;
    }
}
